/*
 * Copyright [2018] [Alex/libo(liboware@gmail.com)]
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.hyts.aop.proxy;

import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;

import com.hyts.aop.AopProxy;
import com.hyts.aop.annotation.Aspect;
import com.hyts.aop.annotation.Inject;
import com.hyts.aop.annotation.Weave;
import com.hyts.aop.error.AopException;
import com.hyts.aop.facade.AopCglibExecutor;
import com.hyts.aop.facade.AopJdkExecutor;
import com.hyts.aop.pointcut.ScanAop;
import com.hyts.aop.pointcut.ScanPointcut;
import lombok.Getter;
import lombok.Setter;
import lombok.extern.slf4j.Slf4j;
import org.apache.tools.ant.util.CollectionUtils;

/**
 * @author LiBo/Alex
 * @since TODO引入特定改变发布版本
 * @version V1.0
 */
public class AopContext {

    /**
     * Aop 代理 對象 集合
     */
    private static final ConcurrentHashMap<Class<?>, Object> aopProxyObject = new ConcurrentHashMap();
    private static final ConcurrentHashMap<Class<?>, Object> definedAopAndAspectRelatation = new ConcurrentHashMap();
    private static final String DEFAULT_PACKAGE_SPLIT_TOKEN = ",";

    @Setter
    @Getter
    private String scanPackage;
    @Setter
    @Getter
    private String packageSplitToken;
    @Setter
    @Getter
    private List<Object> autoAopWeave;


    /**
     * int初始化操作
     * @param scanPackage
     */
    public AopContext(String scanPackage) {
        this.scanPackage = scanPackage;
        this.packageSplitToken = ",";
        this.autoAopWeave = new LinkedList<>();
    }

    /**
     * init初始化對象
     * @param scanPackage
     * @param packageSplitToken
     */
    public AopContext(String scanPackage, String packageSplitToken) {
        this.scanPackage = scanPackage;
        this.packageSplitToken = packageSplitToken;
        this.autoAopWeave = new LinkedList<>();
    }

    /**
     * init初始化方法
     */
    public void init() {
        //扫描操作，用以处理
        boolean isHasWeave = false;
        //扫描， aspect
        List<Class<?>> aspectClassList = ScanPointcut.scanAspect(scanPackage.split(this.packageSplitToken));
        try {
            for(Class<?> param:aspectClassList){
                if(param.isAnnotationPresent(Aspect.class)) {
                    if(param.getInterfaces().length != 0 ) {
                        if(param.getInterfaces().length > 1){
                            throw new IllegalStateException("扫描到AOP对象的接口操作不唯一");
                        }
                        definedAopAndAspectRelatation.put(param.getInterfaces()[0], param);
                    } else {
                        definedAopAndAspectRelatation.put(param, param);
                    }
                }else {
                    isHasWeave = true;
                    this.autoAopWeave.add(param.newInstance());
                }
            }
        } catch (InstantiationException | IllegalAccessException e) {
            e.printStackTrace();
        }
        if(!isHasWeave){
            throw new IllegalArgumentException("没有监测到任何weave对象");
        }
        //扫描， aop
        List<Class<?>> scanClassList = ScanAop.scanAop(scanPackage.split(this.packageSplitToken));

        //采用非依赖注入的方式 系统没有扫描到任何AOP注解
        if(scanClassList == null || scanClassList.isEmpty()){
            Object[] auo = this.autoAopWeave.parallelStream().sorted
                    (Comparator.comparingInt(v -> v.getClass().getAnnotation(Weave.class).order())).toArray();
            //判断是否关系为空!
            if(definedAopAndAspectRelatation.size() > 0 ){
                definedAopAndAspectRelatation.forEach((k,v)->{
                try {
                   Object proxyObj = null;
                   if(k.isInterface()) {
                        proxyObj = AopJdkExecutor.executeJdkAopBean(((Class<?>)v).newInstance(),auo, k);
                        definedAopAndAspectRelatation.replace(k,proxyObj);
                   }else{
                        proxyObj = AopCglibExecutor.executeCglibAopBean(((Class<?>)v).newInstance(),auo);
                        definedAopAndAspectRelatation.replace(k,proxyObj);
                    }
                    aopProxyObject.put(k, proxyObj);
                } catch (InstantiationException | IllegalAccessException e) {
                    e.printStackTrace();
                }
                });
            }
        }
        //采用依赖注入的方式 系统扫描到AOP注解
        scanClassList.forEach((param) -> {
            try {
                Object paramObj = param.newInstance();
                Field[] var6;
                int var5 = (var6 = paramObj.getClass().getDeclaredFields()).length;

                for(int var4 = 0; var4 < var5; ++var4) {
                    Field field = var6[var4];
                    if(field.isAnnotationPresent(Inject.class)) {
                        Class<?> interfaceClass = field.getType();
                        if(!field.isAccessible()) {
                            field.setAccessible(true);
                        }
                        Class<?> targetClass = ((Inject)field.getAnnotation(Inject.class)).targetClass();
                        if(targetClass == Inject.class) {
                            targetClass = (Class)definedAopAndAspectRelatation.get(interfaceClass);
                        }
                        if(!targetClass.isInterface()) {
                            Object [] auo = this.autoAopWeave.stream().sorted(Comparator.comparingInt(v -> v.getClass().getAnnotation(Weave.class).order())).toArray();
                            if(targetClass.getInterfaces().length > 0 && targetClass.getInterfaces()[0].isAssignableFrom(interfaceClass)) {
                                Object proxyObj = AopJdkExecutor.executeJdkAopBean(targetClass.newInstance(), auo, interfaceClass);
                                field.set(paramObj, proxyObj);
                                definedAopAndAspectRelatation.replace(interfaceClass,proxyObj);
                            } else {
                                Object proxyObj = AopCglibExecutor.executeCglibAopBean(targetClass.newInstance(),auo);
                                field.set(paramObj, proxyObj);
                                definedAopAndAspectRelatation.replace(targetClass,proxyObj);
                            }
                        }
                        aopProxyObject.put(param, paramObj);
                    }
                }

            } catch (IllegalAccessException | InstantiationException var9) {
                throw new AopException(var9);
            }
        });
    }

    public static final <T> T getInvokeProxyObject(Class<T> clazz) {
        return (T) aopProxyObject.get(clazz);
    }


    public final <T> T getInvokeProxyBean(Class<T> clazz) {
        return (T) definedAopAndAspectRelatation.get(clazz);
    }


    public  ConcurrentHashMap<Class<?>, Object> getAopProxyObject(){
        return aopProxyObject;
    }

    public  ConcurrentHashMap<Class<?>, Object> getDefinedAopAndAspectRelatation(){
        return definedAopAndAspectRelatation;
    }

    /**
     * getBean對象操作
     * @param clazz
     * @param <T>
     * @return
     */
    public <T> T getBean(Class<T> clazz){
        return AopContext.getInvokeProxyObject(clazz);
    }


}
